﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Library.Collections.Distributed;
using Library.Distributed;
using INDEX = System.UInt64;

namespace Library.Linq.Distributed
{
    public static class DistributedQueryable
    {
        public static IDistributedQueryable AsDistributed(this IQueryable source)
        {
            IDistributedQueryable distributed = source as IDistributedQueryable;
            if (distributed != null) return distributed;
            return Context.Current.Compose(source);
        }
        public static IDistributedQueryable<TSource> AsDistributed<TSource>(this IQueryable<TSource> source)
        {
            IDistributedQueryable<TSource> distributed = source as IDistributedQueryable<TSource>;
            if (distributed != null) return distributed;
            return Context.Current.Compose(source);
        }

        public static TSource Aggregate<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TSource, TSource>> func)
        {
            return Queryable.Aggregate<TSource>(source, func);
        }
        public static bool All<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return Queryable.All<TSource>(source, predicate);
        }
        public static bool Any<TSource>(this IDistributedQueryable<TSource> source)
        {
            return Queryable.Any<TSource>(source);
        }
        public static bool Any<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return Queryable.Any<TSource>(source, predicate);
        }
        public static IDistributedQueryable<TElement> AsQueryable<TElement>(this IDistributedEnumerable<TElement> source)
        {
            IDistributedQueryable<TElement> queryable = source as IDistributedQueryable<TElement>;
            if (queryable != null) return queryable;
            return Platform.Current.Invoke(source, (IEnumerable<TElement> x) => x.AsQueryable(), (ctx, x) => ctx.Compose(x));
        }
        public static IDistributedQueryable AsQueryable(this IDistributedEnumerable source)
        {
            IDistributedQueryable queryable = source as IDistributedQueryable;
            if (queryable != null) return queryable;
            return Platform.Current.Invoke(source, (IEnumerable x) => x.AsQueryable(), (ctx, x) => ctx.Compose(x));
        }
        public static decimal? Average(this IDistributedQueryable<decimal?> source)
        {
            return Queryable.Average(source);
        }
        public static decimal Average(this IDistributedQueryable<decimal> source)
        {
            return Queryable.Average(source);
        }
        public static double? Average(this IDistributedQueryable<double?> source)
        {
            return Queryable.Average(source);
        }
        public static double Average(this IDistributedQueryable<double> source)
        {
            return Queryable.Average(source);
        }
        public static float? Average(this IDistributedQueryable<float?> source)
        {
            return Queryable.Average(source);
        }
        public static float Average(this IDistributedQueryable<float> source)
        {
            return Queryable.Average(source);
        }
        public static double? Average(this IDistributedQueryable<int?> source)
        {
            return Queryable.Average(source);
        }
        public static double Average(this IDistributedQueryable<int> source)
        {
            return Queryable.Average(source);
        }
        public static double? Average(this IDistributedQueryable<long?> source)
        {
            return Queryable.Average(source);
        }
        public static double Average(this IDistributedQueryable<long> source)
        {
            return Queryable.Average(source);
        }
        public static decimal? Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, decimal?>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static decimal Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, decimal>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static double? Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, double?>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static double Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, double>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static float? Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, float?>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static float Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, float>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static double? Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, int?>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static double Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, int>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static double? Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, long?>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static double Average<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, long>> selector)
        {
            return Queryable.Average(source, selector);
        }
        public static IDistributedQueryable<TResult> Cast<TResult>(this IDistributedQueryable source)
        {
            return (IDistributedQueryable<TResult>)Queryable.Cast<TResult>(source);
        }
        public static IDistributedQueryable<TSource> Concat<TSource>(this IDistributedQueryable<TSource> source1, IEnumerable<TSource> source2)
        {
            return (IDistributedQueryable<TSource>)Queryable.Concat<TSource>(source1, source2);
        }
        public static bool Contains<TSource>(this IDistributedQueryable<TSource> source, TSource item)
        {
            return Queryable.Contains<TSource>(source, item);
        }
        public static uint Count<TSource>(this IDistributedQueryable<TSource> source)
        {
            return (uint)Queryable.Count<TSource>(source);
        }
        public static uint Count<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return source.Where(predicate).Count();
        }
        public static IDistributedQueryable<TSource> DefaultIfEmpty<TSource>(this IDistributedQueryable<TSource> source)
        {
            return (IDistributedQueryable<TSource>)Queryable.DefaultIfEmpty<TSource>(source);
        }
        public static IDistributedQueryable<TSource> DefaultIfEmpty<TSource>(this IDistributedQueryable<TSource> source, TSource defaultValue)
        {
            return (IDistributedQueryable<TSource>)Queryable.DefaultIfEmpty<TSource>(source, defaultValue);
        }
        public static IDistributedQueryable<TSource> Distinct<TSource>(this IDistributedQueryable<TSource> source)
        {
            return (IDistributedQueryable<TSource>)Queryable.Distinct<TSource>(source);
        }
        public static TSource ElementAt<TSource>(this IDistributedQueryable<TSource> source, INDEX index)
        {
            return Queryable.ElementAt<TSource>(source, index);
        }
        public static TSource ElementAtOrDefault<TSource>(this IDistributedQueryable<TSource> source, INDEX index)
        {
            return Queryable.ElementAtOrDefault<TSource>(source, index);
        }
        public static IDistributedQueryable<TSource> Except<TSource>(this IDistributedQueryable<TSource> source1, IEnumerable<TSource> source2)
        {
            return (IDistributedQueryable<TSource>)Queryable.Except<TSource>(source1, source2);
        }
        public static TSource First<TSource>(this IDistributedQueryable<TSource> source)
        {
            return Queryable.First<TSource>(source);
        }
        public static TSource First<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return Queryable.First<TSource>(source, predicate);
        }
        public static TSource FirstOrDefault<TSource>(this IDistributedQueryable<TSource> source)
        {
            return Queryable.FirstOrDefault<TSource>(source);
        }
        public static TSource FirstOrDefault<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return Queryable.FirstOrDefault<TSource>(source, predicate);
        }
        public static IDistributedQueryable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
        {
            return (IDistributedQueryable<IGrouping<TKey, TSource>>)Queryable.GroupBy<TSource, TKey>(source, keySelector);
        }
        public static IDistributedQueryable<TResult> GroupBy<TSource, TKey, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TKey, IEnumerable<TSource>, TResult>> resultSelector)
        {
            return (IDistributedQueryable<TResult>)Queryable.GroupBy<TSource, TKey, TResult>(source, keySelector, resultSelector);
        }
        public static IDistributedQueryable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector)
        {
            return (IDistributedQueryable<IGrouping<TKey, TElement>>)Queryable.GroupBy<TSource, TElement>(source, elementSelector);
        }
        public static IDistributedQueryable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector, Expression<Func<TKey, IEnumerable<TElement>, TResult>> resultSelector)
        {
            return (IDistributedQueryable<TResult>)Queryable.GroupBy<TSource, TKey, TElement, TResult>(source, keySelector, elementSelector, resultSelector);
        }
        public static IDistributedQueryable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IDistributedQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, IEnumerable<TInner>, TResult>> resultSelector)
        {
            return (IDistributedQueryable<TResult>)Queryable.GroupJoin<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector);
        }
        public static IDistributedQueryable<TSource> Intersect<TSource>(this IDistributedQueryable<TSource> source1, IEnumerable<TSource> source2)
        {
            return (IDistributedQueryable<TSource>)Queryable.Intersect<TSource>(source1, source2);
        }
        public static IDistributedQueryable<TResult> Join<TOuter, TInner, TKey, TResult>(this IDistributedQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector)
        {
            return (IDistributedQueryable<TResult>)Queryable.Join<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector);
        }
        public static TSource Last<TSource>(this IDistributedQueryable<TSource> source)
        {
            return Queryable.Last<TSource>(source);
        }
        public static TSource Last<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return Queryable.Last<TSource>(source, predicate);
        }
        public static TSource LastOrDefault<TSource>(this IDistributedQueryable<TSource> source)
        {
            return Queryable.LastOrDefault<TSource>(source);
        }
        public static TSource LastOrDefault<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return Queryable.LastOrDefault<TSource>(source, predicate);
        }
        public static ulong LongCount<TSource>(this IDistributedQueryable<TSource> source)
        {
            return (ulong)Queryable.LongCount<TSource>(source);
        }
        public static ulong LongCount<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return (ulong)Queryable.LongCount<TSource>(source, predicate);
        }
        public static TSource Max<TSource>(this IDistributedQueryable<TSource> source)
        {
            return Queryable.Max<TSource>(source);
        }
        public static TResult Max<TSource, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
        {
            return Queryable.Max<TSource, TResult>(source, selector);
        }
        public static TSource Min<TSource>(this IDistributedQueryable<TSource> source)
        {
            return Queryable.Min<TSource>(source);
        }
        public static TResult Min<TSource, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
        {
            return Queryable.Min<TSource, TResult>(source, selector);
        }
        public static IDistributedQueryable<TResult> OfType<TResult>(this IDistributedQueryable source)
        {
            return (IDistributedQueryable<TResult>)Queryable.OfType<TResult>(source);
        }
        public static IDistributedOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
        {
            return (IDistributedOrderedQueryable<TSource>)Queryable.OrderBy<TSource, TKey>(source, keySelector);
        }
        public static IDistributedOrderedQueryable<TSource> OrderByDescending<TSource, TKey>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
        {
            return (IDistributedOrderedQueryable<TSource>)Queryable.OrderByDescending<TSource, TKey>(source, keySelector);
        }
        public static IDistributedQueryable<TSource> Reverse<TSource>(this IDistributedQueryable<TSource> source)
        {
            return (IDistributedQueryable<TSource>)Queryable.Reverse<TSource>(source);
        }
        public static IDistributedQueryable<TResult> Select<TSource, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, INDEX, TResult>> selector)
        {
            return (IDistributedQueryable<TResult>)Library.Linq.Queryable.Select<TSource, TResult>(source, selector);
        }
        public static IDistributedQueryable<TResult> Select<TSource, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
        {
            return (IDistributedQueryable<TResult>)Queryable.Select<TSource, TResult>(source, selector);
        }
        public static IDistributedQueryable<TResult> SelectMany<TSource, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, IEnumerable<TResult>>> selector)
        {
            return (IDistributedQueryable<TResult>)Queryable.SelectMany<TSource, TResult>(source, selector);
        }
        public static IDistributedQueryable<TResult> SelectMany<TSource, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, INDEX, IEnumerable<TResult>>> selector)
        {
            return (IDistributedQueryable<TResult>)Library.Linq.Queryable.SelectMany<TSource, TResult>(source, selector);
        }
        public static IDistributedQueryable<TResult> SelectMany<TSource, TCollection, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, IEnumerable<TCollection>>> collectionSelector, Expression<Func<TSource, TCollection, TResult>> resultSelector)
        {
            return (IDistributedQueryable<TResult>)Queryable.SelectMany<TSource, TCollection, TResult>(source, collectionSelector, resultSelector);
        }
        public static IDistributedQueryable<TResult> SelectMany<TSource, TCollection, TResult>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, INDEX, IEnumerable<TCollection>>> collectionSelector, Expression<Func<TSource, TCollection, TResult>> resultSelector)
        {
            return (IDistributedQueryable<TResult>)Library.Linq.Queryable.SelectMany<TSource, TCollection, TResult>(source, collectionSelector, resultSelector);
        }
        public static bool SequenceEqual<TSource>(this IDistributedQueryable<TSource> source1, IEnumerable<TSource> source2)
        {
            return Queryable.SequenceEqual<TSource>(source1, source2);
        }
        public static TSource Single<TSource>(this IDistributedQueryable<TSource> source)
        {
            return Queryable.Single<TSource>(source);
        }
        public static TSource Single<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return Queryable.Single<TSource>(source, predicate);
        }
        public static TSource SingleOrDefault<TSource>(this IDistributedQueryable<TSource> source)
        {
            return Queryable.SingleOrDefault<TSource>(source);
        }
        public static TSource SingleOrDefault<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return Queryable.SingleOrDefault<TSource>(source, predicate);
        }
        public static IDistributedQueryable<TSource> Skip<TSource>(this IDistributedQueryable<TSource> source, INDEX count)
        {
            return (IDistributedQueryable<TSource>)Queryable.Skip<TSource>(source, count);
        }
        public static IDistributedQueryable<TSource> SkipWhile<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return (IDistributedQueryable<TSource>)Queryable.SkipWhile<TSource>(source, predicate);
        }
        public static IDistributedQueryable<TSource> SkipWhile<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, INDEX, bool>> predicate)
        {
            return (IDistributedQueryable<TSource>)Queryable.SkipWhile<TSource>(source, predicate);
        }
        public static decimal? Sum(this IDistributedQueryable<decimal?> source)
        {
            return Queryable.Sum(source);
        }
        public static decimal Sum(this IDistributedQueryable<decimal> source)
        {
            return Queryable.Sum(source);
        }
        public static double? Sum(this IDistributedQueryable<double?> source)
        {
            return Queryable.Sum(source);
        }
        public static double Sum(this IDistributedQueryable<double> source)
        {
            return Queryable.Sum(source);
        }
        public static float? Sum(this IDistributedQueryable<float?> source)
        {
            return Queryable.Sum(source);
        }
        public static float Sum(this IDistributedQueryable<float> source)
        {
            return Queryable.Sum(source);
        }
        public static int? Sum(this IDistributedQueryable<int?> source)
        {
            return Queryable.Sum(source);
        }
        public static int Sum(this IDistributedQueryable<int> source)
        {
            return Queryable.Sum(source);
        }
        public static long? Sum(this IDistributedQueryable<long?> source)
        {
            return Queryable.Sum(source);
        }
        public static long Sum(this IDistributedQueryable<long> source)
        {
            return Queryable.Sum(source);
        }
        public static decimal? Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, decimal?>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static decimal Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, decimal>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static double? Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, double?>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static double Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, double>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static float? Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, float?>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static float Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, float>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static int? Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, int?>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static int Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, int>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static long? Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, long?>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static long Sum<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, long>> selector)
        {
            return Queryable.Sum<TSource>(source, selector);
        }
        public static IDistributedQueryable<TSource> Take<TSource>(this IDistributedQueryable<TSource> source, INDEX count)
        {
            return (IDistributedQueryable<TSource>)Queryable.Take<TSource>(source, count);
        }
        public static IDistributedQueryable<TSource> TakeWhile<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return (IDistributedQueryable<TSource>)Queryable.TakeWhile<TSource>(source, predicate);
        }
        public static IDistributedQueryable<TSource> TakeWhile<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, INDEX, bool>> predicate)
        {
            return (IDistributedQueryable<TSource>)Queryable.TakeWhile<TSource>(source, predicate);
        }
        public static IDistributedOrderedQueryable<TSource> ThenBy<TSource, TKey>(this IDistributedOrderedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
        {
            return (IDistributedOrderedQueryable<TSource>)Queryable.ThenBy<TSource, TKey>(source, keySelector);
        }
        public static IDistributedOrderedQueryable<TSource> ThenByDescending<TSource, TKey>(this IDistributedOrderedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
        {
            return (IDistributedOrderedQueryable<TSource>)Queryable.ThenByDescending<TSource, TKey>(source, keySelector);
        }
        public static IDistributedQueryable<TSource> Union<TSource>(this IDistributedQueryable<TSource> source1, IEnumerable<TSource> source2)
        {
            return (IDistributedQueryable<TSource>)Queryable.Union<TSource>(source1, source2);
        }
        public static IDistributedQueryable<TSource> Where<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return (IDistributedQueryable<TSource>)Queryable.Where<TSource>(source, predicate);
        }
        public static IDistributedQueryable<TSource> Where<TSource>(this IDistributedQueryable<TSource> source, Expression<Func<TSource, INDEX, bool>> predicate)
        {
            return (IDistributedQueryable<TSource>)Library.Linq.Queryable.Where<TSource>(source, predicate);
        }
        public static IDistributedQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IDistributedQueryable<TFirst> source1, IDistributedEnumerable<TSecond> source2, Expression<Func<TFirst, TSecond, TResult>> resultSelector)
        {
            return (IDistributedQueryable<TResult>)Queryable.Zip<TFirst, TSecond, TResult>(source1, source2, resultSelector);
        }

        public static bool Analyze<TSource>(this IDistributedQueryable<TSource> source)
        {
            throw new NotImplementedException();
        }
        public static LambdaExpression Explain(this IDistributedQueryable source)
        {
            throw new NotImplementedException();
        }
    }
}
